package service

import (
	"code.huawei.com/tdc-sdk/utils"
	"encoding/json"
	"github.com/gomodule/redigo/redis"
	"github.com/spf13/cast"
	"nft-server/db"
	"nft-server/util"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode"

	"github.com/pkg/errors"

	"nft-server/common"
	"nft-server/config"
	"nft-server/dao"
	"nft-server/logger"
	"nft-server/model"
)

var (
	assetListCacheLen    = 10
	transactionsCacheLen = 10
	//blockSummaryCache    *common.BlockSummary
	//transactionsCache []common.Transaction
)

const (
	RedisTokenId                  = "redisTokenId"
	RedisSellPeriod               = "redisSellPeriod"
	AssetGalleryRedisKey          = "assetGallery"
	BlockSummaryRedisKey          = "blockSummary"
	TransactionsRedisKey          = "Transactions"
	AssetGalleryRedisAliveTimeSec = 6 * 60
	BlockSummaryRedisAliveTimeSec = 1 * 60
	TransactionsRedisAliveTimeSec = 1 * 60
	SharedMode                    = "shared"

	AddCollectionCounter = 1
)

var (
	pool *redis.Pool
	//saramaConfig  	*sarama.Config
	lock sync.Mutex

	//topic         	= "CAR_NUMBER_GO"
)

// 资产信息加了缓存，cache
func Init() {
	dacConfig, err := utils.NewConfig(config.GetConfig().DACConfig.ConfigPath)
	if err != nil {
		logger.Error("load dac config file error: %v", err)
		return
	}
	go func() {
		if dacConfig.Service.Type != SharedMode {
			for {
				_, err := updateAssetListCache(assetListCacheLen)
				if err != nil {
					logger.Error("updateAssetListCache error: %v", err)
				}
				time.Sleep(6 * time.Minute)
			}
		}
	}()
	go func() {
		if dacConfig.Service.Type != SharedMode {
			for {
				updateBlockSummaryCache()
				_, err := updateTransactionsCache(transactionsCacheLen)
				if err != nil {
					logger.Error("updateTransactionsCache error: %v", err)
				}
				time.Sleep(1 * time.Minute)
			}
		}
	}()
}

func GetAssetIdInPrivate(collectionIndex, tokenId string) string {
	var builder strings.Builder
	builder.WriteString(collectionIndex)
	builder.WriteString("#")
	builder.WriteString(tokenId)
	return builder.String()
}

// GetCollectionIndexAndId 从资产ID中切分出collectionIndex和tokenId
func GetCollectionIndexAndId(id string) (string, string) {
	ss := strings.Split(id, "#")
	if len(ss) != 2 {
		return "", ""
	}
	return ss[0], ss[1]
}
func BuildKeyOfSubCollection(collectionIndex, subCollectionIndex string) string {
	partString := []string{collectionIndex, subCollectionIndex, RedisTokenId}
	keyOfCollectionSellPeriod := util.CombineStringWithConnect(partString)
	return keyOfCollectionSellPeriod
}
func BuildKeyOfCollectionSellPeriod(collectionIndex, subCollectionIndex string) string {
	partString := []string{collectionIndex, subCollectionIndex, RedisSellPeriod}
	keyOfCollectionSellPeriod := util.CombineStringWithConnect(partString)
	return keyOfCollectionSellPeriod
}
func BuildValueOfCollectionSellPeriod(saleTime, offShelfTime time.Time) string {
	partString := []string{cast.ToString(saleTime.UnixMilli()), cast.ToString(offShelfTime.UnixMilli())}
	valueOfCollectionSellPeriod := util.CombineStringWithConnect(partString)
	return valueOfCollectionSellPeriod
}
func WriteAllSubCollectionIntoRedis(subCollection []*model.CollectionDetail, userId int, redisPool *redis.Pool) error {
	conn := redisPool.Get()
	defer conn.Close()
	err := conn.Send("MULTI")
	if err != nil {
		return err
	}
	for _, oneSubCollection := range subCollection {
		if oneSubCollection.SubCollectionIndex == util.DefaultSubCollectionIndex {
			continue
		}
		assetRelations, err := GetAssetRelationBySubCollectionIndex(oneSubCollection.CollectionIndex, userId, oneSubCollection.SubCollectionIndex)
		if err != nil {
			logger.Error("GetAssetRelationByCollectionId error: %v", err)
			return err
		}
		//将该用户手上该集合的可用tokenId写入redis
		//使用redis set
		keyOfCollection := BuildKeyOfSubCollection(oneSubCollection.CollectionIndex, cast.ToString(oneSubCollection.SubCollectionIndex))
		keyOfCollectionSellPeriod := BuildKeyOfCollectionSellPeriod(oneSubCollection.CollectionIndex, cast.ToString(oneSubCollection.SubCollectionIndex))
		valueOfCollectionSellPeriod := BuildValueOfCollectionSellPeriod(oneSubCollection.SaleTime, oneSubCollection.OffShelfTime)
		tokenNum := len(assetRelations)
		err = conn.Send("SET", keyOfCollectionSellPeriod, valueOfCollectionSellPeriod)
		for i := 0; i < tokenNum; i++ {
			err = conn.Send("SADD", keyOfCollection, assetRelations[i].TokenId)
		}
	}
	_, err = conn.Do("EXEC")
	if err != nil {
		logger.Error("can't write into redis error: %v", err)
	}
	return nil
}
func WriteSubCollectionIntoRedis(collection *model.CollectionDetail, userId int, redisPool *redis.Pool) error {
	assetRelations, err := GetAssetRelationBySubCollectionIndex(collection.CollectionIndex, userId, collection.SubCollectionIndex)
	if err != nil {
		logger.Error("GetAssetRelationByCollectionId error: %v", err)
		return err
	}
	//将该用户手上该集合的可用tokenId写入redis
	conn := redisPool.Get()
	defer conn.Close()
	//使用redis set
	keyOfCollection := BuildKeyOfSubCollection(collection.CollectionIndex, cast.ToString(collection.SubCollectionIndex))
	//写入集合出售时间
	keyOfCollectionSellPeriod := BuildKeyOfCollectionSellPeriod(collection.CollectionIndex, cast.ToString(collection.SubCollectionIndex))
	valueOfCollectionSellPeriod := BuildValueOfCollectionSellPeriod(collection.SaleTime, collection.OffShelfTime)
	//批量写入
	tokenNum := len(assetRelations)
	err = conn.Send("MULTI")
	if err != nil {
		return err
	}
	err = conn.Send("SET", keyOfCollectionSellPeriod, valueOfCollectionSellPeriod)
	for i := 0; i < tokenNum; i++ {
		err = conn.Send("SADD", keyOfCollection, assetRelations[i].TokenId)
	}
	_, err = conn.Do("EXEC")
	if err != nil {
		logger.Error("can't write into redis error: %v", err)
	}
	return nil
}
func DeleteAllSubCollectionFromRedis(subCollections []*model.CollectionDetail, redisPool *redis.Pool) error {
	conn := redisPool.Get()
	defer conn.Close()
	for _, collection := range subCollections {
		if collection.SubCollectionIndex == util.DefaultSubCollectionIndex {
			continue
		}
		//使用redis set
		keyOfCollection := BuildKeyOfSubCollection(collection.CollectionIndex, cast.ToString(collection.SubCollectionIndex))
		//集合出售时间
		keyOfCollectionSellPeriod := BuildKeyOfCollectionSellPeriod(collection.CollectionIndex, cast.ToString(collection.SubCollectionIndex))
		lastNum, _ := redis.Int(conn.Do("SCARD", keyOfCollection))
		err := conn.Send("MULTI")
		if err != nil {
			return err
		}
		err = conn.Send("DEL", keyOfCollectionSellPeriod)
		err = conn.Send("SPOP", keyOfCollection, lastNum)
		if err != nil {
			logger.Error("can't delete value from redis error: %v", err)
		}
		_, err = conn.Do("EXEC")
		if err != nil {
			logger.Error("can't delete value from redis error: %v", err)
		}
	}
	return nil
}
func DeleteSubCollectionFromRedis(subCollection *model.CollectionDetail, redisPool *redis.Pool) error {
	conn := redisPool.Get()
	defer conn.Close()
	//使用redis set
	keyOfCollection := BuildKeyOfSubCollection(subCollection.CollectionIndex, cast.ToString(subCollection.SubCollectionIndex))
	//集合出售时间
	keyOfCollectionSellPeriod := BuildKeyOfCollectionSellPeriod(subCollection.CollectionIndex, cast.ToString(subCollection.SubCollectionIndex))
	lastNum, _ := redis.Int(conn.Do("SCARD", keyOfCollection))
	err := conn.Send("MULTI")
	if err != nil {
		return err
	}
	err = conn.Send("DEL", keyOfCollectionSellPeriod)
	err = conn.Send("SPOP", keyOfCollection, lastNum)
	_, err = conn.Do("EXEC")
	if err != nil {
		logger.Error("can't delete value from redis error: %v", err)
	}
	return nil
}
func WriteOneTokenIntoRedis(collectionIndex string, subCollectionIndex int, tokenId string, redisPool *redis.Pool) error {
	conn := redisPool.Get()
	defer conn.Close()
	//使用redis set
	keyOfCollection := BuildKeyOfSubCollection(collectionIndex, cast.ToString(subCollectionIndex))
	_, err := conn.Do("SADD", keyOfCollection, tokenId)
	if err != nil {
		logger.Error("can't write into redis error: %v", err)
	}
	return nil
}
func CheckNowInSaleFromRedis(collectionIndex string, subCollectionIndex int, redisPool *redis.Pool) bool {
	conn := redisPool.Get()
	defer conn.Close()
	//集合出售时间
	keyOfCollectionSellPeriod := BuildKeyOfCollectionSellPeriod(collectionIndex, cast.ToString(subCollectionIndex))
	valueOfCollectionSellPeriod, err := redis.String(conn.Do("GET", keyOfCollectionSellPeriod))
	if err != nil {
		return false
	}
	partString := util.GetStringFromConnect(valueOfCollectionSellPeriod)
	saleTime := time.UnixMilli(cast.ToInt64(partString[0]))
	offShelfTime := time.UnixMilli(cast.ToInt64(partString[1]))
	nowTime := time.Now()
	//出售中
	if nowTime.After(saleTime) && nowTime.Before(offShelfTime) {
		return true
	}
	return false
}

//ApplyAssets 领取藏品
func ApplyAssets(collectionIndex string, subCollectionIndex int, tokenId string, userId int) (txId string, err error) {
	//更新链上资产关系
	queryUserParameter := common.QueryUserDetail{
		UserId: &userId,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		return "", errors.Errorf("GetUserInfo error: %v", err)
	}
	admin, err := dao.GetUserDao().GetAdminUser()
	if err != nil {
		return "", errors.Errorf("GetAdminUser error: %v", err)
	}
	txId, err = NFTTransfer(collectionIndex, admin.Address, user.Address, tokenId)
	if err != nil {
		print("collectionIndex:", collectionIndex, "token:", tokenId)
		return "", errors.Errorf("NFTTransfer error: %v", err)
	}
	//更新表中资产关系,由于没有二级交易，每次更新对象必然不同
	err = UpdateAssetRelation(collectionIndex, tokenId, userId)
	if err != nil {
		return "", errors.Errorf("UpdateAssetRelation error: %v", err)
	}

	//更新collection表中,领取一个，已领取数量加一，更新管理员已领取数据
	lock.Lock()
	err = UpdateCollectionCounter(admin.Id, collectionIndex, subCollectionIndex, AddCollectionCounter)
	err = UpdateCollectionCounter(admin.Id, collectionIndex, util.DefaultSubCollectionIndex, AddCollectionCounter)
	//更新用户已领取数量
	err = UpdateAssetNum(cast.ToInt(userId), 1)
	lock.Unlock()
	if err != nil {
		return "", errors.Errorf("UpdateCollectionCounter error: %v", err)
	}

	lastAsset, _ := dao.GetAssetDao().GetAsset(GetAssetIdInPrivate(collectionIndex, cast.ToString(tokenId)))

	asset := &model.Asset{
		IdWithCollectionIndex: lastAsset.IdWithCollectionIndex,
		TransactionId:         txId,
		Owner:                 userId,
		CreatedTime:           time.Now(),
		SaveUrl:               lastAsset.SaveUrl,
		Category:              lastAsset.Category,
		AssetID:               lastAsset.AssetID,
	}
	err = dao.GetAssetDao().UpdateAsset(asset)
	if err != nil {
		return "", errors.Errorf("UpdateAsset error: %v", err)
	}
	return txId, nil
}

// TransferAssets 资产转赠
func TransferAssets(collectionIndex, tokenId string, toUserId int, toUserAddress string, fromUserInfo *model.User) (txId string, err error) {
	//更新链上资产关系
	adminInfo, err := GetAdmin()
	if err != nil {
		return "", errors.Errorf("NFT Transfer error: %v", err)
	}
	if adminInfo.Id == cast.ToInt(toUserId) {
		return "", errors.Errorf("NFT Transfer error: cannot transfer to admin")
	}
	//非管理员 切用户
	if adminInfo.Id != fromUserInfo.Id {
		err = SwitchIdentityWithPem(fromUserInfo.Cert, fromUserInfo.Key)
		if err != nil {
			logger.Error("Transfer asset error: fail to switch identity with pem to user")
			return "", errors.Errorf("NFT Transfer error: %v", err)
		}
		defer SwitchIdentityWithPem(adminInfo.Cert, adminInfo.Key)

	}

	txId, err = NFTTransfer(collectionIndex, fromUserInfo.Address, toUserAddress, tokenId)
	if err != nil {
		logger.Error("collectionIndex: ", collectionIndex, "token:", tokenId)
		return "", errors.Errorf("NFTTransfer error: %v", err)
	}

	//更新表中资产关系,由于没有二级交易，每次更新对象必然不同
	err = UpdateAssetRelation(collectionIndex, tokenId, toUserId)
	if err != nil {
		return "", errors.Errorf("UpdateAssetRelation error: %v", err)
	}

	lastAsset, _ := dao.GetAssetDao().GetAsset(GetAssetIdInPrivate(collectionIndex, cast.ToString(tokenId)))

	asset := &model.Asset{
		IdWithCollectionIndex: lastAsset.IdWithCollectionIndex,
		TransactionId:         txId,
		Owner:                 toUserId,
		CreatedTime:           time.Now(),
		SaveUrl:               lastAsset.SaveUrl,
		Category:              lastAsset.Category,
		AssetID:               lastAsset.AssetID,
	}
	err = dao.GetAssetDao().UpdateAsset(asset)
	if err != nil {
		return "", errors.Errorf("UpdateAsset error: %v", err)
	}

	if adminInfo.Id != fromUserInfo.Id {
		err = SwitchIdentityWithPem(adminInfo.Cert, adminInfo.Key)
	}
	if err != nil {
		logger.Error("Transfer asset error: fail to switch identity with pem to Admin")
		return "", errors.Errorf("NFT Transfer error: %v", err)
	}
	return txId, nil
}

func UpdateAssets(collectionIndex, startTokenId string, userId int, txId, saveUrl string, categoryId, tokenIdAmount int) error {
	tmpTokenId := cast.ToInt(startTokenId)
	assetAll := make([]*model.Asset, tokenIdAmount)
	for tokenId := tmpTokenId; tokenId < tmpTokenId+tokenIdAmount; tokenId++ {
		assetTokenId := cast.ToString(tokenId)

		assetId := sdk.GenerateAssetID(collectionIndex, assetTokenId)

		index := categoryId - 1
		if index < 0 {
			index = 0
		}
		//数组下标从0开始
		asset := &model.Asset{
			IdWithCollectionIndex: GetAssetIdInPrivate(collectionIndex, assetTokenId),
			TransactionId:         txId,
			Owner:                 userId,
			CreatedTime:           time.Now(),
			SaveUrl:               saveUrl,
			Category: &model.Category{
				Id:   categoryId,
				Name: Categories[index],
			},
			AssetID: assetId,
		}
		assetIndex := tokenId - tmpTokenId
		if assetIndex >= 0 && assetIndex < tokenIdAmount {
			assetAll[assetIndex] = asset
		}

	}
	_, err := dao.GetAssetDao().InsertMultiAsset(assetAll)
	if err != nil {
		return errors.Errorf("SaveAsset error in UpdateAssets: %v", err)
	}
	return nil
}
func DeleteAsset(collectionIndex, tokenId string) error {
	assetId := GetAssetIdInPrivate(collectionIndex, tokenId)
	return DeleteAssetWithId(assetId)
}
func DeleteAssetWithId(IdWithCollectionIndex string) error {
	return dao.GetAssetDao().DeleteAssets(IdWithCollectionIndex)
}

//获取藏品的urlS
func GetAssetWebURl(saveUrl string) (string, error) {
	return util.GetAssetUrl(saveUrl)
}

//判断是否为共享版
func IsSharedMode() (bool, error) {
	dacConfig, err := utils.NewConfig(config.GetConfig().DACConfig.ConfigPath)
	if err != nil {
		return true, err
	}
	if dacConfig.Service.Type != SharedMode {
		//专享版
		return false, nil
	}
	return true, nil
}

//获取该用户领取藏品
func GetAssets(userId int, page, pageSize int) ([]common.Asset, int, error) {
	queryAssets, size, err := dao.GetAssetDao().GetAssetsByUserId(userId, page, pageSize)
	if err != nil {
		logger.Error("GetAssetsByUserId error :%v", err)
		return nil, 0, err
	}
	var assetsReq []common.Asset

	dacConfig, err := utils.NewConfig(config.GetConfig().DACConfig.ConfigPath)

	for _, oneAsset := range queryAssets {
		collectionIndex, tokenId := GetCollectionIndexAndId(oneAsset.IdWithCollectionIndex)
		url, err := GetAssetWebURl(oneAsset.SaveUrl)
		if err != nil {
			logger.Error("GetAssets error: %v", err)
			return nil, 0, err
		}
		//根据共享专享返回ID
		//共享版返回链上资产ID
		//专享版返回自用检验页面ID
		reqAssetId := oneAsset.AssetID
		if dacConfig.Service.Type != SharedMode {
			//专享版
			reqAssetId = GetAssetIdInPrivate(collectionIndex, tokenId)
		}
		assetRelation, err := GetAssetOwner(collectionIndex, tokenId)
		if err != nil {
			logger.Error("GetAssets error: %v", err)
			return nil, 0, err
		}
		asset := common.Asset{
			CollectionIndex:    collectionIndex,
			SubCollectionIndex: assetRelation.SubCollectionIndex,
			AssetId:            reqAssetId,
			TokenId:            tokenId,
			PictureUrl:         url,
			Category:           &common.Category{CategoryId: oneAsset.Category.Id, CategoryName: oneAsset.Category.Name},
		}
		assetsReq = append(assetsReq, asset)
	}
	return assetsReq, size, nil
}

//获取资产
func GetAsset(collectionIndex, tokenId string) (*model.Asset, error) {
	assetId := GetAssetIdInPrivate(collectionIndex, tokenId)
	asset, err := dao.GetAssetDao().GetAsset(assetId)
	if err != nil {
		return nil, errors.Errorf("GetAsset error: %v", err)
	}
	return asset, nil
}

func GetAssetByAssetId(assetId string) (*model.Asset, error) {
	asset, err := dao.GetAssetDao().GetAssetByAssetId(assetId)
	if err != nil {
		return nil, errors.Errorf("GetAssetByAssetId error: %v", err)
	}
	return asset, nil
}
func GetAssetId(collectionIndex, tokenId string) (string, error) {
	asset, err := GetAsset(collectionIndex, tokenId)
	if err != nil {
		return util.InvalidString, errors.Errorf("GetAsset error: %v", err)
	}
	dacConfig, err := utils.NewConfig(config.GetConfig().DACConfig.ConfigPath)
	if err != nil {
		return util.InvalidString, errors.Errorf("GetAsset error: %v", err)
	}
	//根据共享专享返回ID
	//共享版返回链上资产ID
	//专享版返回自用检验页面ID
	reqAssetId := asset.AssetID
	if dacConfig.Service.Type != SharedMode {
		//专享版
		reqAssetId = GetAssetIdInPrivate(collectionIndex, tokenId)
	}
	return reqAssetId, nil
}

//获取资产详情--用于DAC核验页面
func GetAssetDetail(asset *model.Asset) (*common.AssetDetail, error) {
	tran, err := GetTransaction(asset.TransactionId)
	if err != nil {
		return nil, errors.Errorf("GetTransaction error: %v", err)
	}

	owner, err := GetUserAddressById(cast.ToInt(asset.Owner))
	if err != nil {
		return nil, errors.Errorf("GetUserAddressById error: %v", err)
	}

	assetDetail := &common.AssetDetail{
		Owner:   owner,
		Time:    tran.Time,
		TxId:    asset.TransactionId,
		BlockId: tran.BlockID,
	}
	return assetDetail, nil
}

//获取区块链信息
func GetBlockchain() (*common.BlockSummary, error) {
	blockSummary := &common.BlockSummary{}
	// 从redis中获取cache
	data, err := redis.Bytes(db.GetRedisValue(BlockSummaryRedisKey))
	if err != nil {
		return nil, errors.Errorf("GetBlockchain error: %v", err)
	}
	if err := json.Unmarshal(data, &blockSummary); err != nil {
		logger.Error("Blockchain Unmarshal error: %v", err)
		return nil, err
	}
	admin, err := GetAdmin()
	if err != nil {
		logger.Error("Get Admin error: %v", err)
		return nil, err
	}
	blockSummary.StartTime = admin.CreatedTime
	return blockSummary, nil
}

func GetTransactions(num int) ([]common.Transaction, error) {
	var transactions []common.Transaction
	if num > transactionsCacheLen {
		var err error
		transactions, err = updateTransactionsCache(num)
		if err != nil {
			return nil, err
		}
	}
	if num <= transactionsCacheLen {
		// 从redis中获取cache
		data, err := redis.Bytes(db.GetRedisValue(TransactionsRedisKey))
		if err != nil {
			return nil, errors.Errorf("read redis for Transactions error: %v", err)
		}
		if err := json.Unmarshal(data, &transactions); err != nil {
			logger.Error("Transactions Unmarshal error: %v", err)
			return nil, err
		}
		return transactions[:num], nil
	}
	return transactions, nil
}

func GetAssetList(num int) ([]common.AssetGallery, error) {

	var assetGallerys []common.AssetGallery
	if num > assetListCacheLen {
		var err error
		assetGallerys, err = updateAssetListCache(num)
		if err != nil {
			return nil, err
		}
	}
	if num <= assetListCacheLen {
		// 从redis中获取cache
		data, err := redis.Bytes(db.GetRedisValue(AssetGalleryRedisKey))
		if err != nil {
			return nil, errors.Errorf("read redis for AssetGallery error: %v", err)
		}
		if err := json.Unmarshal(data, &assetGallerys); err != nil {
			logger.Error("assetGallerys Unmarshal error: %v", err)
			return nil, err
		}
		return assetGallerys[:num], nil
	}
	return assetGallerys, nil
}

func updateAssetListCache(num int) ([]common.AssetGallery, error) {
	// 获取指定数量的最近交易资产
	assets, err := dao.GetAssetDao().GetAssets(num)
	if err != nil {
		logger.Error("updateAssetListCache GetAssets error: %v", err)
		return nil, err
	}

	var assetsRes []common.AssetGallery
	for _, eachAsset := range assets {
		parts := strings.Split(eachAsset.IdWithCollectionIndex, "#")
		if len(parts) != 2 {
			logger.Error("updateAssetListCache Invalid token names [%v]", eachAsset.IdWithCollectionIndex)
			continue
		}

		picUrl, err := GetAssetPicUrl(parts[0], parts[1], eachAsset.SaveUrl)
		if err != nil {
			logger.Error("updateAssetListCache read picture error: %v", err)
			continue
		}

		ownerName, err := maskAssetOwner(eachAsset.Owner)
		if err != nil {
			logger.Error("updateAssetListCache read picture error: %v", err)
			continue
		}
		asset := common.AssetGallery{
			Collection: parts[0],
			Id:         parts[0] + "#" + parts[1],
			PictureUrl: picUrl,
			Owner:      ownerName,
			Time:       eachAsset.CreatedTime.Format(time.RFC3339),
		}
		assetsRes = append(assetsRes, asset)
	}

	// 把数据转成json传入redis
	res, err := json.MarshalIndent(assetsRes, "", "\t")
	if err != nil {
		logger.Error("updateAssetListCache read picture error: %v", err)
		return nil, err
	}
	if err := db.SetRedisKeyValue(AssetGalleryRedisKey, string(res), AssetGalleryRedisAliveTimeSec); err != nil {
		logger.Error(" redis set AssetGallery error: %v", err)
		return nil, err
	}
	if num > assetListCacheLen {
		assetListCacheLen = num
	}
	return assetsRes, nil
}

func maskAssetOwner(userId int) (string, error) {
	queryUserParameter := common.QueryUserDetail{
		UserId: &userId,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		logger.Error(" maskAssetOwner error: %v", err)
		return "", err
	}
	if user == nil {
		logger.Error(" can not find user by id : " + cast.ToString(userId))
		return "", err
	}

	name := user.Name
	res := ""
	firstNumPos := 0
	for i, n := range name {
		res += string(n)
		if unicode.IsNumber(n) {
			firstNumPos = i
			break
		}
	}
	for i := firstNumPos + 1; i < len(name)-1; i++ {
		res += string('*')
	}
	res += string(name[len(name)-1])
	return res, nil
}

func updateBlockSummaryCache() {
	summary, err := GetBlockSummary()
	if err != nil {
		logger.Error("GetBlockSummary error: %v", err)
		return
	}
	blockSummaryCache := &common.BlockSummary{
		Blocks: strconv.FormatInt(int64(summary.Blocks), 10),
		Txs:    strconv.FormatInt(int64(summary.Txs), 10),
	}
	// 把数据转成json传入redis
	res, err := json.MarshalIndent(blockSummaryCache, "", "\t")
	if err != nil {
		logger.Error("updateBlockSummaryCache  error: %v", err)
		return
	}
	if err := db.SetRedisKeyValue(BlockSummaryRedisKey, string(res), BlockSummaryRedisAliveTimeSec); err != nil {
		logger.Error(" updateBlockSummaryCache error: %v", err)
		return
	}
}

func updateTransactionsCache(num int) ([]common.Transaction, error) {
	blockStart := int64(0)
	blockSummary, err := GetBlockSummary()
	if err != nil {
		logger.Error(" updateTransactionsCache error: %v", err)
		return nil, err
	}
	blocks := blockSummary.Blocks
	if err != nil {
		logger.Error("updateTransactionsCache ParseInt error: %v", err)
		return nil, err
	}

	blockEnd := int64(blocks) - 1
	if int64(num) < blockEnd {
		blockStart = blockEnd - int64(num)
	}

	blockList, err := GetBlockList(blockStart, blockEnd)
	if err != nil {
		logger.Error("GetBlockList error: %v", err)
		return nil, err
	}
	var transactions []common.Transaction
	transactions, err = getTransactionsHelper(blockList, num)
	if err != nil {
		logger.Error("updateTransactionsCache  error: %v", err)
		return nil, err
	}
	// 把数据转成json传入redis
	res, err := json.MarshalIndent(transactions, "", "\t")
	if err != nil {
		logger.Error("updateTransactionsCache  error: %v", err)
		return nil, err
	}
	if err := db.SetRedisKeyValue(TransactionsRedisKey, string(res), TransactionsRedisAliveTimeSec); err != nil {
		logger.Error(" updateTransactionsCache error: %v", err)
		return nil, err
	}
	if num > transactionsCacheLen {
		transactionsCacheLen = num
	}
	return transactions, nil
}

func getTransactionsHelper(blockList *BlockList, num int) (transactions []common.Transaction, err error) {
	var count int
	for i := blockList.Count - 1; i >= 0; i-- {
		for j := blockList.Blocks[i].TxsCount - 1; j >= 0; j-- {
			transaction := common.Transaction{
				BlockID: strconv.FormatInt(int64(blockList.Blocks[i].Txs[j].BlockID), 10),
				TxId:    blockList.Blocks[i].Txs[j].TxId,
				Time:    blockList.Blocks[i].Txs[j].CreateTime,
			}
			transactions = append(transactions, transaction)
			count += 1
			// 达到指定的数量
			if count >= num {
				// 把数据转成json传入redis
				res, err := json.MarshalIndent(transactions, "", "\t")
				if err != nil {
					return nil, err
				}
				if err := db.SetRedisKeyValue(TransactionsRedisKey, string(res), TransactionsRedisAliveTimeSec); err != nil {
					return nil, err
				}
				if num > transactionsCacheLen {
					transactionsCacheLen = num
				}
				return transactions, nil
			}
		}
	}
	return nil, nil
}

// BuildResponseDataForGetAssetDetail 用于帮助函数GetAssetDetail构建返回数据
func BuildResponseDataForGetAssetDetail(userId string, req common.AssetDetailReq) (response *common.AssetDetailInfo, err error) {
	assetRelation, err := GetAssetOwner(req.CollectionIndex, req.TokenId)
	if err != nil {
		logger.Error("GetAssetOwner error: %v", err)
		return nil, err
	}
	collection, err := GetCollectionDetailByCollection(req.CollectionIndex, assetRelation.SubCollectionIndex)
	if err != nil {
		logger.Error("BuildResponseDataForGetAssetDetail error: %v", err)
		return nil, err
	}
	//未铸造完成没有图片
	metaData, err := GetMetaDataWithCollectionIndexAndTokenIdWithoutUserId(req.CollectionIndex, req.TokenId)
	if err != nil || metaData == nil {
		logger.Error("BuildResponseDataForGetAssetDetail error: %v", err)
		return nil, err
	}
	reqAssetId, err := GetAssetId(req.CollectionIndex, req.TokenId)
	if err != nil {
		logger.Error("BuildResponseDataForGetAssetDetail error: %v", err)
		return nil, err
	}

	metaDetail := MetaDataDetail{}
	if err = json.Unmarshal([]byte(metaData.TokenURI), &metaDetail); err != nil {
		logger.Error("GetCollectionForPublish error: %v", err)
		return nil, err
	}
	AssetUrl, err := GetAssetWebURl(metaDetail.AssetUrl)
	if err != nil {
		logger.Error("GetCollectionForPublish error: %v", err)
		return nil, err
	}
	properties := GetValueFromMetaDataProperties(&metaDetail)
	response = new(common.AssetDetailInfo)
	response.CollectionIndex = collection.CollectionIndex
	response.SubCollectionIndex = collection.SubCollectionIndex
	response.TokenId = req.TokenId
	response.Supplies = cast.ToInt(metaData.Amount)
	response.CollectionName = collection.CollectionName
	response.AssetId = reqAssetId
	response.Url = AssetUrl
	response.Description = metaDetail.Description
	response.Category = GetCategoryNameById(cast.ToInt(metaDetail.Category))
	response.Name = metaDetail.Name
	response.Issuer = properties[PropertiesOfIssuer]
	response.Currency = collection.Currency
	response.Price = collection.Price
	return response, nil
}
